package com.asen.commons.core.jdbc.builder.mysql;

import com.asen.commons.core.jdbc.param.SqlColumn;
import com.asen.commons.core.jdbc.param.SqlContext;
import com.asen.commons.core.jdbc.param.SqlContextManager;
import com.asen.commons.core.meta.dao.AggrFuncItem;
import com.asen.commons.core.meta.dao.Condition;
import com.asen.commons.core.meta.dao.LimitItem;
import com.asen.commons.core.meta.dao.OrderItem;
import com.asen.commons.core.meta.entity.EntityObject;
import com.asen.commons.core.meta.entity.FieldObject;
import com.asen.commons.core.meta.enums.AggrFuncEnum;
import com.asen.commons.core.meta.loader.EntityObjectLoader;
import com.asen.commons.utils.BaseAssert;
import com.asen.commons.utils.CollectionUtils;
import com.asen.commons.utils.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * select查询
 *
 * @author Gary
 * @version 1.0
 * @date 2021-08-22
 */
public class SelectBuilder {

    private static final String SELECT_ALL_SYMBOL = "*";

    private EntityObjectLoader loader = EntityObjectLoader.getInstance();

    private List<String> selectList = new ArrayList<>();
    private List<String> orderByList = new ArrayList<>();
    private List<String> groupByList = new ArrayList<>();

    private StringBuilder aggrFuncSql = new StringBuilder();
    private StringBuilder limitSql = new StringBuilder();

    private List<String> selectItems = null;
    private List<String> excludeItems = null;
    private List<String> customItems = null;

    private List<AggrFuncItem> aggrFuncs = null;
    private List<OrderItem> orderByItems = null;
    private List<String> groupByItems = null;
    private List<String> collectByItems = null;

    private SqlContext context;
    private SqlContextManager contextManager;

    public SelectBuilder(SqlContext context) {
        this.context = context;
        this.contextManager = SqlContextManager.newInstance(context);
    }

    public StringBuilder getSelectSql() {
        StringBuilder sql = joinSqlSection(selectList);
        if (aggrFuncSql.length() > 0) {
            if (sql.length() > 0) {
                sql.append(", ");
            }
            sql.append(aggrFuncSql);
        }

        BaseAssert.check(sql.length() > 0, "没有有效的select属性");
        return sql;
    }

    public StringBuilder getGroupBySql() {
        return joinSqlSection(groupByList);
    }

    public StringBuilder getOrderBySql() {
        return joinSqlSection(orderByList);
    }

    public StringBuilder getLimitSql() {
        return limitSql;
    }

    public void build() throws Exception {
        buildSelectList(getDefaultList(context.getMainEntity()));
    }

    public void build(Condition condition) throws Exception {
        BaseAssert.notNull(condition, "条件condition不能为空");

        aggrFuncs = condition.getAggrFuncItems();
        orderByItems = condition.getOrderItems();
        groupByItems = condition.getGroupByItems();
        collectByItems = condition.getCollectByItems();
        selectItems = condition.getSelectItems().getSelectList();
        excludeItems = condition.getSelectItems().getExcludeList();
        customItems = condition.getSelectItems().getCustomList();

        buildAggrFunc();
        buildSelect();
        buildOrderBy();
        buildGroupBy();
        buildLimit(condition);
    }

    /**
     * 聚合函数
     */
    private void buildAggrFunc() {
        if (aggrFuncs == null) {
            return;
        }

        for (AggrFuncItem aggrFunc : aggrFuncs) {
            String fieldName = aggrFunc.getFieldName();
            SqlColumn sqlColumn = contextManager.getSqlColumn(fieldName);
            String columnWithAlias = sqlColumn == null ? null : sqlColumn.getFullColumn();
            if (SELECT_ALL_SYMBOL.equals(fieldName)) {
                if (!AggrFuncEnum.COUNT.equals(aggrFunc.getSymbol())) {
                    throw new IllegalArgumentException("函数" + aggrFunc.getSymbol().getDesc() + "(*)不合法！");
                }
                columnWithAlias = "*";
            } else if (sqlColumn == null) {
                throw new IllegalArgumentException("属性字段" + aggrFunc.getSymbol().getDesc() + "(" + fieldName + ")不存在");
            }

            String alias = aggrFunc.getAlias();
            if (StringUtils.isEmpty(alias)) {
                alias = fieldName;
            }

            if (aggrFuncSql.length() > 0) {
                aggrFuncSql.append(", ");
            }

            aggrFuncSql.append(aggrFunc.getSymbol().getDesc());
            aggrFuncSql.append("(");
            aggrFuncSql.append(columnWithAlias);
            aggrFuncSql.append(")");
            aggrFuncSql.append(" AS \"");
            aggrFuncSql.append(alias);
            aggrFuncSql.append("\"");
        }
    }

    private void buildGroupBy() {
        if (CollectionUtils.isEmpty(groupByItems)) {
            return;
        }

        for (String fieldName : groupByItems) {
            if (isAggrFuncField(fieldName) || isCollectByField(fieldName)) {
                continue;
            }

            SqlColumn sqlColumn = contextManager.getSqlColumn(fieldName);
            String columnWithAlias = sqlColumn == null ? null : sqlColumn.getFullColumn();
            if (columnWithAlias == null) {
                continue;
            }

            if (!groupByList.contains(columnWithAlias)) {
                groupByList.add(columnWithAlias);
            }
        }
    }

    private void buildSelect() {
        List<String> selectList = selectItems;
        if (CollectionUtils.isEmpty(selectList)) {
            if (hasAggrFunc()) {
                return;
            }
            selectList = getDefaultList(context.getMainEntity());
        }
        buildSelectList(selectList);
        buildCustomSelect();
    }

    private void buildSelectList(List<String> selectList) {
        for (String select : selectList) {
            buildSelect(select);
        }
    }

    private void buildCustomSelect() {
        if (CollectionUtils.isEmpty(customItems)) {
            return;
        }

        for (String customItem : customItems) {
            String customSql = parseCustom(customItem);
            if (StringUtils.isBlank(customSql)) {
                continue;
            }

            if (!selectList.contains(customSql)) {
                selectList.add(customSql);
            }
        }
    }

    private String parseCustom(String sql) {
        if (StringUtils.isEmpty(sql)) {
            return "";
        }

        int beginIndex = sql.indexOf("${");
        if (beginIndex == -1) {
            return sql;
        }

        StringBuilder result = new StringBuilder();
        result.append(sql.substring(0, beginIndex));
        int endIndex = sql.indexOf("}");
        String fieldName = sql.substring(beginIndex + 2, endIndex);
        SqlColumn sqlColumn = contextManager.getSqlColumn(fieldName);
        BaseAssert.notNull(sqlColumn, "属性变量字段${" + fieldName + "}不存在！");
        result.append(sqlColumn.getFullColumn());
        result.append(parseCustom(sql.substring(endIndex + 1)));
        return result.toString();
    }

    private List<String> getDefaultList(EntityObject eo) {
        return getDefaultList(eo, "");
    }

    private List<String> getDefaultList(EntityObject eo, String prefix) {
        List<String> result = new ArrayList<>();
        for (FieldObject fo : eo.getFieldList()) {

            if (fo.isExtFiled()) {
                continue;
            }

            if (fo.isIgnore()) {
                continue;
            }

            // 不处理集合类型属性
            if (fo.isCollection()) {
                continue;
            }

            // 非本地属性
            //if (fo.isRemoteReference()) {
            //    continue;
            //}

            if (isIgnoreSelect(fo.getName())) {
                continue;
            }

            // 简单属性
            if (fo.isSimpleType()) {
                result.add(prefix + fo.getName());
                continue;
            }

            // 数据字典类型
            //if (fo.isDictItem()) {
            //    result.add(prefix + fo.getName());
            //    continue;
            //}

            // 关联属性，默认取ID/编码/名称
            //if (!fo.isFieldSubordinate()) {
            //    // TODO 原来的逻辑是否写反了?
            //    EntityObject refEo = loader.get(fo.getJavaType());
            //    for (FieldObject f : refEo.getFieldList()) {
            //        if (f.getName().equals("id")
            //                || f.getName().equals("number")
            //                || f.getName().equals("name")) {
            //            result.add(prefix + fo.getName() + "." + f.getName());
            //        }
            //    }
            //}
        }

        return result;
    }

    private void buildSelect(String fullName) {
        if (SELECT_ALL_SYMBOL.equals(fullName)) {
            buildSelectList(getDefaultList(context.getMainEntity()));
            return;
        }

        SqlColumn sqlColumn = contextManager.getSqlColumn(fullName);
        String columnWithAlias = sqlColumn == null ? null : sqlColumn.getFullColumn();
        if (columnWithAlias == null) {
            return;
        }

        if (isIgnoreSelect(fullName)) {
            return;
        }

        addGroupByIfHasAggrFunc(fullName);
        addGroupByIfHasCollect(fullName);

        String select = new StringBuilder()
                .append(columnWithAlias)
                .append(" AS \"")
                .append(fullName)
                .append("\"")
                .toString();

        if (!selectList.contains(select)) {
            selectList.add(select);
        }
    }

    private void buildOrderBy() {
        if (CollectionUtils.isEmpty(orderByItems)) {
            return;
        }

        for (OrderItem item : orderByItems) {
            SqlColumn sqlColumn = contextManager.getSqlColumn(item.getFieldName());
            String columnWithAlias = sqlColumn == null ? null : sqlColumn.getFullColumn();
            if (columnWithAlias == null) {
                continue;
            }

            if (isAggrFuncField(item.getFieldName())) {
                continue;
            }

            if (isCollectByField(item.getFieldName())) {
                continue;
            }

            addGroupByIfHasAggrFunc(item.getFieldName());
            addGroupByIfHasCollect(item.getFieldName());

            String orderBy = columnWithAlias + item.getSymbol().getDesc();
            if (!orderByList.contains(orderBy)) {
                orderByList.add(orderBy);
            }
        }
    }

    private void buildLimit(Condition condition) {
        LimitItem limitItem = condition.getLimitItem();
        if (limitItem != null) {
            if (limitItem.getOffset() != null) {
                limitSql.append(limitItem.getOffset());
                limitSql.append(",");
            }
            limitSql.append(limitItem.getRows());
            return;
        }

        if (condition.isPage()) {
            int pageNum = condition.getPageNum();
            int pageSize = condition.getPageSize();
            int offset = (pageNum - 1) * pageSize;
            if (offset <= 0) {
                offset = 0;
            }
            limitSql.append(offset);
            limitSql.append(",");
            limitSql.append(pageSize);
        }
    }

    private void addGroupByIfHasAggrFunc(String fieldName) {
        if (isAggrFuncField(fieldName)) {
            if (groupByItems == null) {
                groupByItems = new ArrayList<>();
            }

            if (!groupByItems.contains(fieldName)) {
                groupByItems.add(fieldName);
            }
        }
    }

    private void addGroupByIfHasCollect(String fieldName) {
        if (CollectionUtils.isNotEmpty(collectByItems) && !collectByItems.contains(fieldName)) {
            if (groupByItems == null) {
                groupByItems = new ArrayList<>();
            }

            if (!groupByItems.contains(fieldName)) {
                groupByItems.add(fieldName);
            }
        }
    }

    private boolean hasAggrFunc() {
        return aggrFuncs != null && aggrFuncs.size() > 0;
    }

    private boolean isAggrFuncField(String fieldName) {
        return hasAggrFunc() && aggrFuncs.stream().anyMatch(x -> x.getFieldName().equals(fieldName));
    }

    private boolean isCollectByField(String fieldName) {
        return CollectionUtils.isNotEmpty(collectByItems) && collectByItems.contains(fieldName);
    }

    private boolean isExcludeField(String fieldName) {
        return CollectionUtils.isNotEmpty(excludeItems) && excludeItems.contains(fieldName);
    }

    private boolean isIgnoreSelect(String fieldName) {
        if (isAggrFuncField(fieldName)) {
            return true;
        }

        if (isCollectByField(fieldName)) {
            return true;
        }

        if (isExcludeField(fieldName)) {
            return true;
        }

        return false;
    }

    private StringBuilder joinSqlSection(List<String> list) {
        StringBuilder sql = new StringBuilder();
        if (list.size() > 0) {
            for (String str : list) {
                if (sql.length() > 0) {
                    sql.append(", ");
                }
                sql.append(str);
            }
        }
        return sql;
    }
}
